package nl.weeaboo.sh.model;

import java.awt.Point;
import java.awt.Rectangle;
import java.util.Collection;
import java.util.HashMap;
import java.util.Map;
import java.util.SortedSet;
import java.util.TreeMap;
import java.util.Map.Entry;

import timon.common.xml.XmlElement;

public class RoomModel {

	private String id;
	private Rectangle rect;
	private Map<Integer, Map<Point, TileSetIndex>> tiles;
	private Map<Point, RoomEnemyModel> enemies;
	private boolean leftWall, rightWall, topWall, bottomWall;
	private boolean leftOpen, rightOpen, topOpen, bottomOpen;
	
	public RoomModel(String id) {
		this.id = id;
		
		rect = new Rectangle(0, 0, 0, 0);
		tiles = new TreeMap<Integer, Map<Point, TileSetIndex>>();
		tiles.put(0, new HashMap<Point, TileSetIndex>());
		
		enemies = new HashMap<Point, RoomEnemyModel>();
	}
	public RoomModel(RoomModel other) {
		this((String)null);
		
		rect = new Rectangle(other.rect);
		for (Entry<Integer, Map<Point, TileSetIndex>> ots : other.tiles.entrySet()) {
			Map<Point, TileSetIndex> ts = getLayer(ots.getKey());
			for (Entry<Point, TileSetIndex> entry : ots.getValue().entrySet()) {
				ts.put(new Point(entry.getKey()), entry.getValue().clone());
			}
		}

		for (Entry<Point, RoomEnemyModel> entry : other.enemies.entrySet()) {
			enemies.put(new Point(entry.getKey()), entry.getValue().clone());
		}
		
		setWallSides(other.leftWall, other.rightWall, other.topWall, other.bottomWall);
		setOpenSides(other.leftOpen, other.rightOpen, other.topOpen, other.bottomOpen);
	}
	
	//Functions
	public RoomModel clone() { return new RoomModel(this); }
	
	public boolean contains(int x, int y) { return rect.contains(x, y); }
	
	//Getters
	public String getId() { return id; }
	
	public boolean isLeftWall() { return leftWall; }
	public boolean isRightWall() { return rightWall; }
	public boolean isTopWall() { return topWall; }
	public boolean isBottomWall() { return bottomWall; }
	
	public boolean isLeftOpen() { return leftOpen; }
	public boolean isRightOpen() { return rightOpen; }
	public boolean isTopOpen() { return topOpen; }
	public boolean isBottomOpen() { return bottomOpen; }
	
	public int getX() { return rect.x; }
	public int getY() { return rect.y; }
	public int getWidth() { return rect.width; }
	public int getHeight() { return rect.height; }
	public Rectangle getBounds() { return rect; }
	public Map<Point, TileSetIndex> getLayer(int layer) {
		Map<Point, TileSetIndex> map = tiles.get(layer);
		if (map == null) {
			tiles.put(layer, map = new HashMap<Point, TileSetIndex>());
		}
		return map;
	}
	public TileSetIndex getTile(int layer, Point p) { return getLayer(layer).get(p); }
	public Map<Point, TileSetIndex> getTiles(int layer) { return getLayer(layer); }
	public Collection<Point> getTilePositions(int layer) { return getLayer(layer).keySet(); }
	public Collection<TileSetIndex> getTileValues(int layer) { return getLayer(layer).values(); }
	public SortedSet<Integer> getLayerIndices() { return (SortedSet<Integer>)tiles.keySet(); }
	public Map<Point, RoomEnemyModel> getEnemies() { return enemies; }
	public RoomEnemyModel getEnemy(Point p) { return enemies.get(p); }
	
	//Setters
	public void setId(String id) { this.id = id; }
	public void setOffset(int x, int y) { rect.x = x; rect.y = y; }
	public void setSize(int w, int h) { rect.width = w; rect.height = h; }
	public void setTile(int layer, int x, int y, TileSetIndex index) {
		if (index == null) {
			getLayer(layer).remove(new Point(x, y));
		} else {
			getLayer(layer).put(new Point(x, y), index.clone());
		}		
	}	
	public void setRoomEnemy(int x, int y, RoomEnemyModel e) {
		if (e == null) {
			enemies.remove(new Point(x, y));
		} else {
			enemies.put(new Point(x, y), e.clone());
		}
	}
	public void setOpenSides(boolean leftOpen, boolean rightOpen, boolean topOpen, boolean bottomOpen) {
		this.leftOpen = leftOpen;
		this.rightOpen = rightOpen;
		this.topOpen = topOpen;
		this.bottomOpen = bottomOpen;
	}
	public void setWallSides(boolean leftWall, boolean rightWall, boolean topWall, boolean bottomWall) {
		this.leftWall = leftWall;
		this.rightWall = rightWall;
		this.topWall = topWall;
		this.bottomWall = bottomWall;
		
		leftOpen   &= !leftWall;
		rightOpen  &= !rightWall;
		topOpen    &= !topWall;
		bottomOpen &= !bottomWall;
	}

	//Save Support
	public void save(XmlElement parentE) {
		XmlElement e = parentE.addChild("room");

		e.addChild("id", id);
		
		e.addChild("x", rect.x);
		e.addChild("y", rect.y);
		e.addChild("width", rect.width);
		e.addChild("height", rect.height);
		
		e.addChild("leftOpen", leftOpen);
		e.addChild("rightOpen", rightOpen);
		e.addChild("topOpen", topOpen);
		e.addChild("bottomOpen", bottomOpen);
		
		e.addChild("leftWall", leftWall);
		e.addChild("rightWall", rightWall);
		e.addChild("topWall", topWall);
		e.addChild("bottomWall", bottomWall);
		
		XmlElement enemiesE = e.addChild("enemies");
		for (Entry<Point, RoomEnemyModel> entry : enemies.entrySet()) {
			XmlElement enemyE = enemiesE.addChild("enemy");
			enemyE.addAttribute("x", entry.getKey().x);
			enemyE.addAttribute("y", entry.getKey().y);
			entry.getValue().saveInto(enemyE);
		}
		
		//XmlElement tilesE = e.addChild("tiles");
		
		StringBuilder sb = new StringBuilder();
		for (Entry<Integer, Map<Point, TileSetIndex>> layer : tiles.entrySet()) {
			
			sb.delete(0, sb.length());
			for (Entry<Point, TileSetIndex> entry : layer.getValue().entrySet()) {
				sb.append(String.format("(%d,%d) %s:%d:%d\n", entry.getKey().x, entry.getKey().y,
						entry.getValue().getTileSet().getId(), entry.getValue().getX(), entry.getValue().getY()));
			}

			XmlElement layerE = e.addChild("layer", sb.toString());
			layerE.addAttribute("index", layer.getKey());
		}
	}
}
